﻿Imports System
Imports System.CodeDom
Imports System.Collections
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.ComponentModel.Design.Serialization
Imports System.Drawing
Imports System.Reflection
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Compiler

Namespace System.Workflow.ComponentModel.Serialization
    Public Class ActivityCodeDomSerializationManager
        Implements IDesignerSerializationManager, IServiceProvider
        ' Events
        Public Custom Event ResolveName As ResolveNameEventHandler
        Public Custom Event SerializationComplete As EventHandler

        ' Methods
        Public Sub New(ByVal manager As IDesignerSerializationManager)
            If (manager Is Nothing) Then
                Throw New ArgumentNullException("manager")
            End If
            Me.serializationManager = manager
            Me.serviceContainer = New ServiceContainer
            AddHandler Me.serializationManager.ResolveName, New ResolveNameEventHandler(AddressOf Me.OnResolveName)
            AddHandler Me.serializationManager.SerializationComplete, New EventHandler(AddressOf Me.OnSerializationComplete)
            If TypeOf Me.serializationManager Is DesignerSerializationManager Then
                AddHandler DirectCast(Me.serializationManager, DesignerSerializationManager).SessionDisposed, New EventHandler(AddressOf Me.OnSessionDisposed)
            End If
        End Sub

        Public Sub AddSerializationProvider(ByVal provider As IDesignerSerializationProvider)
            Me.serializationManager.AddSerializationProvider(provider)
        End Sub

        Public Function CreateInstance(ByVal type As Type, ByVal arguments As ICollection, ByVal name As String, ByVal addToContainer As Boolean) As Object
            Return Me.serializationManager.CreateInstance(type, arguments, name, False)
        End Function

        Public Function GetInstance(ByVal name As String) As Object
            Return Me.serializationManager.GetInstance(name)
        End Function

        Public Function GetName(ByVal value As Object) As String
            Dim name As String = Nothing
            Dim activity As Activity = [TryCast](value,Activity)
            If (Not activity Is Nothing) Then
                If (activity.Parent Is Nothing) Then
                    name = [TryCast](activity.GetValue(WorkflowMarkupSerializer.XClassProperty),String)
                    If ((Not name Is Nothing) AndAlso (name.LastIndexOf(".") > 0)) Then
                        name = name.Substring((name.LastIndexOf("."c) + 1))
                    End If
                Else
                    name = activity.QualifiedName.Replace("."c, "_"c)
                End If
            End If
            If (name Is Nothing) Then
                name = Me.serializationManager.GetName(value)
            End If
            Return name
        End Function

        Public Function GetSerializer(ByVal objectType As Type, ByVal serializerType As Type) As Object
            If (objectType Is GetType(String)) Then
                Return PrimitiveCodeDomSerializer.Default
            End If
            If (((Not objectType Is Nothing) AndAlso TypeProvider.IsAssignable(GetType(ICollection([Of] String)), objectType)) AndAlso (Not objectType.IsArray AndAlso (serializerType Is GetType(CodeDomSerializer)))) Then
                Dim descriptor As PropertyDescriptor = [TryCast](Me.Context.Item(GetType(PropertyDescriptor)),PropertyDescriptor)
                If (Not descriptor Is Nothing) Then
                    If (String.Equals(descriptor.Name, "SynchronizationHandles", StringComparison.Ordinal) AndAlso TypeProvider.IsAssignable(GetType(Activity), descriptor.ComponentType)) Then
                        Return New SynchronizationHandlesCodeDomSerializer
                    End If
                Else
                    Dim context As ExpressionContext = [TryCast](Me.Context.Item(GetType(ExpressionContext)),ExpressionContext)
                    If (((Not context Is Nothing) AndAlso TypeOf context.Expression Is CodePropertyReferenceExpression) AndAlso String.Equals(DirectCast(context.Expression, CodePropertyReferenceExpression).PropertyName, "SynchronizationHandles", StringComparison.Ordinal)) Then
                        Return New SynchronizationHandlesCodeDomSerializer
                    End If
                End If
            End If
            Dim serializer As Object = Me.serializationManager.GetSerializer(objectType, serializerType)
            If Not Me.UseUserDefinedSerializer(objectType, serializerType) Then
                serializer = New SerializableTypeCodeDomSerializer([TryCast](serializer,CodeDomSerializer))
            End If
            Return serializer
        End Function

        Public Function GetService(ByVal serviceType As Type) As Object
            Dim service As Object = Nothing
            If (serviceType Is GetType(IReferenceService)) Then
                service = New ActivityCodeDomReferenceService([TryCast](Me.serializationManager.GetService(serviceType),IReferenceService))
            End If
            If (serviceType Is GetType(IServiceContainer)) Then
                service = Me.serializationManager.GetService(serviceType)
                If (service Is Nothing) Then
                    service = Me.serviceContainer
                End If
            End If
            If (service Is Nothing) Then
                service = Me.serializationManager.GetService(serviceType)
            End If
            If (service Is Nothing) Then
                service = Me.serviceContainer.GetService(serviceType)
            End If
            Return service
        End Function

        Public Function [GetType](ByVal typeName As String) As Type
            Dim type As Type = Me.serializationManager.GetType(typeName)
            If (type Is Nothing) Then
                Dim service As ITypeProvider = [TryCast](Me.GetService(GetType(ITypeProvider)),ITypeProvider)
                If (Not service Is Nothing) Then
                    type = service.GetType(typeName)
                End If
            End If
            Return type
        End Function

        Private Sub OnResolveName(ByVal sender As Object, ByVal e As ResolveNameEventArgs)
            If (Not Me.resolveNameEventHandler Is Nothing) Then
                Me.resolveNameEventHandler.Invoke(Me, e)
            End If
        End Sub

        Private Sub OnSerializationComplete(ByVal sender As Object, ByVal e As EventArgs)
            If (Not Me.serializationCompleteEventHandler Is Nothing) Then
                Me.serializationCompleteEventHandler.Invoke(Me, e)
            End If
        End Sub

        Private Sub OnSessionDisposed(ByVal sender As Object, ByVal e As EventArgs)
            Try 
                If (Not Me.serializationCompleteEventHandler Is Nothing) Then
                    Me.serializationCompleteEventHandler.Invoke(Me, EventArgs.Empty)
                End If
            Finally
                Me.resolveNameEventHandler = Nothing
                Me.serializationCompleteEventHandler = Nothing
            End Try
        End Sub

        Public Sub RemoveSerializationProvider(ByVal provider As IDesignerSerializationProvider)
            Me.serializationManager.RemoveSerializationProvider(provider)
        End Sub

        Public Sub ReportError(ByVal errorInformation As Object)
            Me.serializationManager.ReportError(errorInformation)
        End Sub

        Public Sub SetName(ByVal instance As Object, ByVal name As String)
            If (Not Me.GetInstance(name) Is instance) Then
                Me.serializationManager.SetName(instance, name)
            End If
        End Sub

        Private Function UseUserDefinedSerializer(ByVal objectType As Type, ByVal serializerType As Type) As Boolean
            If ((objectType Is Nothing) OrElse (serializerType Is Nothing)) Then
                Return True
            End If
            If (Not objectType.IsSerializable OrElse (Not serializerType Is GetType(CodeDomSerializer))) Then
                Return True
            End If
            If ((objectType.IsPrimitive OrElse objectType.IsEnum) OrElse ((objectType Is GetType(String)) OrElse GetType(Activity).IsAssignableFrom(objectType))) Then
                Return True
            End If
            Dim converter As TypeConverter = TypeDescriptor.GetConverter(objectType)
            If ((Not converter Is Nothing) AndAlso converter.CanConvertTo(GetType(InstanceDescriptor))) Then
                Return True
            End If
            Dim serializer As Object = Me.serializationManager.GetSerializer(objectType, serializerType)
            If (((Not serializer.GetType.Assembly Is GetType(CodeDomSerializer).Assembly) AndAlso (Not serializer.GetType.Assembly Is Assembly.GetExecutingAssembly)) AndAlso (Not serializer.GetType.Assembly Is Assembly.Load("System.Workflow.Activities, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"))) Then
                Return True
            End If
            Dim activity As Activity = [TryCast](Me.serializationManager.Context.Item(GetType(Activity)),Activity)
            Return ((((Not activity Is Nothing) AndAlso (Not activity.Site Is Nothing)) AndAlso ((Not activity.Site.Container Is Nothing) AndAlso (Not objectType.Namespace Is Nothing))) AndAlso objectType.Namespace.Equals(GetType(Image).Namespace))
        End Function


        ' Properties
        Public ReadOnly Property Context As ContextStack
            Get
                Return Me.serializationManager.Context
            End Get
        End Property

        Public ReadOnly Property Properties As PropertyDescriptorCollection
            Get
                Return Me.serializationManager.Properties
            End Get
        End Property

        Protected Property SerializationManager As IDesignerSerializationManager
            Get
                Return Me.serializationManager
            End Get
            Set(ByVal value As IDesignerSerializationManager)
                Me.serializationManager = value
            End Set
        End Property


        ' Fields
        Private serializationManager As IDesignerSerializationManager
        Private serviceContainer As ServiceContainer
    End Class
End Namespace

